package forge

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"gomod.alauda.cn/mysql-mgr-operator/pkg/constant"
	corev1 "k8s.io/api/core/v1"
	"satellite/pkg/remoting"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type OfflineOption struct {
	SelectAll       bool
	SelectPrimary   bool
	SelectSecondary bool
	SelectOrdinal   []int
	IsForce         bool
}

type Offline struct {
	*OfflineOption
	*Base
	password string
}

func NewOffline(opt *OfflineOption) *Offline {
	return &Offline{
		OfflineOption: opt,
	}
}

func (o *Offline) WithBase(base *Base) *Offline {
	o.Base = base
	return o
}

func (o *Offline) Run() error {
	if o.IsForce {
		err := o.deletePods()
		if err != nil {
			return err
		}
		return nil
	}
	password, err := o.getPassword("root")
	if err != nil {
		return err
	}
	o.password = password
	err = o.stopGR()
	if err != nil {
		return err
	}
	return nil
}

func (o *Offline) stopGR() error {
	opt := client.MatchingLabels{
		constant.LabelOfCluster: o.Name,
	}
	podCol := make([]corev1.Pod, 0)
	if o.SelectAll {
		pods, err := o.ListPods(opt)
		if err != nil {
			return err
		}
		podCol = append(podCol, pods.Items...)
	}
	if o.SelectPrimary {
		opt[constant.LabelOfRole] = constant.ValueOfRolePrimary
		pods, err := o.ListPods(opt)
		if err != nil {
			return err
		}
		podCol = append(podCol, pods.Items...)
	}
	if o.SelectSecondary {
		opt[constant.LabelOfRole] = constant.ValueOfRoleSecondary
		pods, err := o.ListPods(opt)
		if err != nil {
			return err
		}
		podCol = append(podCol, pods.Items...)
	}
	for _, ordinal := range o.SelectOrdinal {
		name := fmt.Sprintf("%v-%v", o.Name, ordinal)
		pod, err := o.getPod(name)
		if err != nil {
			return err
		}
		podCol = append(podCol, *pod)
	}
	for _, pod := range podCol {
		err := o.remotingStopGr(pod.Name)
		if err != nil {
			return err
		}
	}
	return nil
}

func (o *Offline) remotingStopGr(name string) error {
	exec, err := remoting.NewRemoteExec(o.Namespace, "pods", name, "mysql")
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), o.Timeout)
	defer cancel()
	stdio, _, err := exec.Exec(ctx, []string{"mysql", "-uroot", "-p" + o.password, "-e", "stop group_replication;"})
	if err != nil {
		return errors.WithMessage(err, stdio.String())
	}
	return nil
}

func (o *Offline) deletePods() error {
	opt := client.MatchingLabels{
		constant.LabelOfCluster: o.Name,
	}
	noGrace := client.GracePeriodSeconds(0)
	if o.SelectAll {
		err := o.deleteAllOfPods(opt, noGrace)
		if err != nil {
			return err
		}
		log.Info().Str("pod", "all").Msg("delete success")
		return nil
	}
	if o.SelectPrimary {
		opt[constant.LabelOfRole] = constant.ValueOfRolePrimary
		err := o.deleteAllOfPods(opt, noGrace)
		if err != nil {
			return err
		}
		log.Info().Str("role", constant.ValueOfRolePrimary).Msg("delete success")
	}
	if o.SelectSecondary {
		opt[constant.LabelOfRole] = constant.ValueOfRoleSecondary
		err := o.deleteAllOfPods(opt, noGrace)
		if err != nil {
			return err
		}
		log.Info().Str("role", constant.ValueOfRoleSecondary).Msg("delete success")
	}
	for _, ordinal := range o.SelectOrdinal {
		name := fmt.Sprintf("%v-%v", o.Name, ordinal)
		pod, err := o.getPod(name)
		if err != nil {
			return err
		}
		err = o.delete(pod, noGrace)
		if err != nil {
			return err
		}
		log.Info().Str("pod", name).Msg("delete success")
	}
	return nil
}
